#include "SceneMesh.h"

#include "SceneNode.h"
#include "CheckError.h"

#include "Material.h"
#include "MVPShader.h"
#include "Shader.h"
#include "Texture.h"

#include "WRenderer.h"

int SceneMesh::meshCreated = 0;

SceneMesh::SceneMesh(std::string material)
{
	//this->parentNode = node;
	//node->AddMesh(std::shared_ptr<SceneMesh>(this));
	if(material != "")
	{
		if(Material::materials.find(material) != Material::materials.end())
		{
			this->mat = Material::materials.at(material);
		}
	}

	drawID = meshCreated;
	meshCreated++;

	initiated = false;
	enabled = true;
	meshType = SceneMesh::POSITION | SceneMesh::NORMAL | SceneMesh::INDEX | SceneMesh::UV0;
}

SceneMesh::~SceneMesh()
{
	indexArray.reset();

	vertexPosArray.reset();
	vertexNormArray.reset();
	vertexUVArray.reset();
	vertexDrawIDArray.reset();
}

//called from loading thread
void SceneMesh::GenVBO()
{
	vboIDs = std::unique_ptr<GLuint[]>(new GLuint[4]);

	glGenVertexArrays(1, &vaoID);
	glBindVertexArray(vaoID);

	glGenBuffers(4, vboIDs.get());

	//bind vertex buffers
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[0]);
	glBufferData(GL_ARRAY_BUFFER, numOfTriangles * 3 * 3 * sizeof(GL_FLOAT), vertexPosArray.get(), GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *) nullptr);
	
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[1]);
	glBufferData(GL_ARRAY_BUFFER, numOfTriangles * 3 * 1 * sizeof(unsigned int), vertexDrawIDArray.get(), GL_STATIC_DRAW);
	glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, (GLubyte *) nullptr);

	if((meshType & SceneMesh::NORMAL) > 0)
	{
		glEnableVertexAttribArray(2);
		glBindBuffer(GL_ARRAY_BUFFER, vboIDs[2]);
		glBufferData(GL_ARRAY_BUFFER, numOfTriangles * 3 * 3 * sizeof(GL_FLOAT), vertexNormArray.get(), GL_STATIC_DRAW);
		glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *) nullptr);
	}

	if((meshType & SceneMesh::UV0) > 0)
	{
		glEnableVertexAttribArray(3);
		glBindBuffer(GL_ARRAY_BUFFER, vboIDs[3]);
		glBufferData(GL_ARRAY_BUFFER, numOfTriangles * 3 * 2 * sizeof(GL_FLOAT), vertexUVArray.get(), GL_STATIC_DRAW);
		glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, (GLubyte *) nullptr);
	}

	//bind index buffer
	if((meshType & SceneMesh::INDEX) > 0)
	{
		glGenBuffers(1, &iboID);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, numOfTriangles * 3 * sizeof(GL_UNSIGNED_INT), indexArray.get(), GL_STATIC_DRAW);
	}
	glBindVertexArray(0);

	initiated = true;
}

void SceneMesh::Render(int pass)
{
	if(pass == WRenderer::SHADOWPASS && !mat->castShadow)
	{
		return;
	}

	RenderMesh(pass);	
}

void SceneMesh::RenderMesh(int pass)
{
	if(enabled)
	{
		std::shared_ptr<Shader> shader = mat->GetShader();
		mat->Bind();
		glBindVertexArray(vaoID);
		if(meshType & SceneMesh::INDEX)
		{
			glDrawElements(GL_TRIANGLES, numOfTriangles * 3, GL_UNSIGNED_INT, (void*)0);
		}
		glBindVertexArray(0);
		mat->Unbind();
	}
}
	